package com.koron.order.experiment.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.koron.bean.base.Attachment;
import com.koron.bean.query.PageQuery;
import com.koron.bean.system.staff.po.AppUserPo;
import com.koron.common.core.business.common.service.AttachmentService;
import com.koron.common.core.business.workflow.templates.WorkFlowHandler;
import com.koron.common.core.util.CodeTools;
import com.koron.common.core.util.message.MessageUtil;
import com.koron.order.common.bean.base.OrderType;
import com.koron.order.common.bean.po.*;
import com.koron.order.common.service.*;
import com.koron.order.common.tools.CommonCodeTools;
import com.koron.order.common.tools.OrderCodeEnum;
import com.koron.order.experiment.bean.convertor.ExperimentConvertor;
import com.koron.order.experiment.bean.dto.ExperimentDetailDto;
import com.koron.order.experiment.bean.po.ExperimentEquipmentPo;
import com.koron.order.experiment.bean.po.ExperimentPo;
import com.koron.order.experiment.bean.po.ExperimentReadyPo;
import com.koron.order.experiment.bean.po.ExperimentStatus;
import com.koron.order.experiment.bean.query.ExperimentQuery;
import com.koron.order.experiment.bean.vo.EquipmentConclusionVo;
import com.koron.order.experiment.bean.vo.ExperimentDetailVo;
import com.koron.order.experiment.bean.vo.ExperimentVo;
import com.koron.order.experiment.bean.vo.ReviewVo;
import com.koron.order.experiment.feign.PlanIssueFeignService;
import com.koron.order.experiment.mapper.ExperimentMapper;
import com.koron.order.experiment.service.ExperimentEquipmentService;
import com.koron.order.experiment.service.ExperimentExecuteService;
import com.koron.order.experiment.service.ExperimentReadyService;
import com.koron.order.experiment.service.ExperimentService;
import com.koron.order.maintain.bean.entity.MaintainStatus;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * (Experiment)表服务实现类
 *
 * @author zhouj
 * @since 2021-10-08 16:25:05
 */
@Service("experimentService")
public class ExperimentServiceImpl implements ExperimentService {

    @Autowired
    private ExperimentReadyService experimentReadyService;

    @Autowired
    private ExperimentEquipmentService experimentEquipmentService;

    @Autowired
    private OrderInstructionService orderInstructionService;

    @Autowired
    private ExperimentConvertor experimentConvertor;

    @Autowired
    private ExperimentExecuteService executeService;

    @Autowired
    OrderExecuteInfoService orderExecuteInfoService;

    @Autowired
    private WorkFlowHandler workFlowHandler;

    @Autowired
    private OrderExecuteService orderExecuteService;

    @Autowired
    private PlanIssueFeignService planIssueFeignService;

    @Autowired
    private OrderEquipService orderEquipService;

    //作业单位
    @Autowired
    private OrderWorkDeptService workDeptService;

    //附件
    @Autowired
    private AttachmentService attachmentService;

    /*工单的工作地点*/
    @Autowired
    OrderLocationService orderLocationService;

    @Autowired
    MessageUtil messageUtil;

    @Override
    @TaskAnnotation("insertOrUpdate")
    public int insertOrUpdate(SessionFactory factory, ExperimentPo experiment) {

        ExperimentMapper mapper = factory.getMapper(ExperimentMapper.class);

        return mapper.insertOrUpdate(experiment);
    }

    @Override
    @TaskAnnotation("insertOrUpdateBatch")
    public int insertOrUpdateBatch(SessionFactory factory, List<ExperimentPo> experimentList) {

        ExperimentMapper mapper = factory.getMapper(ExperimentMapper.class);

        if (CollectionUtils.isEmpty(experimentList)) {
            return 0;
        }
        experimentList.forEach(p -> {
            if (StringUtils.isEmpty(p.getId())) {
                p.setId(CodeTools.getCode32());
            }
            p.setCreateBy(getCurrUser().getAccount());
            p.setCreateByName(getCurrUser().getName());
        });

        return mapper.insertOrUpdateBatch(experimentList);
    }

    @Override
    @TaskAnnotation("insert")
    public int insert(SessionFactory factory, ExperimentPo experiment) {

        ExperimentMapper mapper = factory.getMapper(ExperimentMapper.class);

        return mapper.insert(experiment);
    }

    @Override
    @TaskAnnotation("insertBatch")
    public int insertBatch(SessionFactory factory, List<ExperimentPo> experiments) {

        ExperimentMapper mapper = factory.getMapper(ExperimentMapper.class);

        if (CollectionUtils.isEmpty(experiments)) {
            return 0;
        }
        experiments.forEach(p -> {
            if (StringUtils.isEmpty(p.getId())) {
                p.setId(CodeTools.getCode32());
            }
            p.setCreateBy(getCurrUser().getAccount());
            p.setCreateByName(getCurrUser().getName());
        });

        return mapper.insertBatch(experiments);
    }

    @Override
    @TaskAnnotation("update")
    public int update(SessionFactory factory, ExperimentPo experiment) {

        Assert.hasText(experiment.getId(), "主键id不能为空");

        ExperimentMapper mapper = factory.getMapper(ExperimentMapper.class);

        return mapper.update(experiment);
    }

    @Override
    @TaskAnnotation("updateStatus")
    public boolean updateStatus(SessionFactory factory, String id, String status) {
        ExperimentMapper mapper = factory.getMapper(ExperimentMapper.class);
        ExperimentPo experimentPo = new ExperimentPo();
        experimentPo.setId(id);
        experimentPo.setStatus(status);
        return mapper.update(experimentPo) > 0;
    }

    @Override
    @TaskAnnotation("deleteById")
    public int deleteById(SessionFactory factory, String id) {

        ExperimentMapper mapper = factory.getMapper(ExperimentMapper.class);
        //顺便删除作业表单的数据
        return mapper.deleteById(id);
    }

    @Override
    @TaskAnnotation("queryById")
    public ExperimentDetailVo queryById(SessionFactory factory, String id) {

        //1.1 试验id 查询详情
        ExperimentMapper mapper = factory.getMapper(ExperimentMapper.class);
        ExperimentPo experimentPo = mapper.queryById(id);

        ExperimentDetailVo experimentDetailVo = experimentConvertor.beanToExperimentDetailVo(experimentPo);

        //1.2 作业准备
        List<ExperimentReadyPo> experimentReadyPoList = experimentReadyService.queryByOrderId(factory, id);

        ExperimentEquipmentPo experimentEquipmentPo = new ExperimentEquipmentPo();
        experimentEquipmentPo.setExperimentId(id);

        //1.3 作业过程
        List<ExperimentEquipmentPo> experimentEquipmentPos = experimentEquipmentService.queryList(factory, experimentEquipmentPo);

        //1.4 作业指导书
        List<OrderInstructionBean> orderInstructionBeans = orderInstructionService.queryByBizId(factory, id);

        //1.5 工单设备
        List<OrderEquipPo> orderEquipPos = orderEquipService.queryByOrderId(factory, id);

        //1.6 工单工作地点
        List<OrderLocationPo> orderLocationPos = orderLocationService.queryByOrderId(factory, id);

        experimentDetailVo.setExperimentReadyPoList(experimentReadyPoList);
        experimentDetailVo.setExperimentEquipmentPoList(experimentEquipmentPos);
        experimentDetailVo.setOrderInstructionBeanList(orderInstructionBeans);
        experimentDetailVo.setOrderEquipPoList(orderEquipPos);
        experimentDetailVo.setOrderLocationList(orderLocationPos);

        return experimentDetailVo;
    }

    @Override
    @TaskAnnotation("queryList")
    public List<ExperimentVo> queryList(SessionFactory factory, ExperimentQuery query) {

        ExperimentMapper mapper = factory.getMapper(ExperimentMapper.class);

        //主要sql
        List<ExperimentVo> vos = mapper.queryList(query);

        if (CollectionUtils.isNotEmpty(vos)) {

            vos.forEach(p -> {
                String trafficLight = CommonCodeTools.calcTrafficLight(p.getFactEndTime(), p.getStartTime(), p.getEndTime(), p.getStatus());
                p.setTrafficLight(trafficLight);
            });

            //----------------以下采用批量工单ids查询提升效率

            List<String> orderIds = vos.stream().map(ExperimentVo::getId).collect(Collectors.toList());

            //1.1 查询工单的作业单位
            List<OrderWorkDeptPo> orderWorkDeptPos = workDeptService.queryByOrderIds(factory, orderIds);
            //设置进列表
            if (CollectionUtils.isNotEmpty(orderWorkDeptPos)) {
                Map<String, List<OrderWorkDeptPo>> cond = orderWorkDeptPos.stream().collect(Collectors.groupingBy(OrderWorkDeptPo::getOrderId));
                for (ExperimentVo experimentVo : vos) {
                    experimentVo.setWorkDeptPoList(cond.getOrDefault(experimentVo.getId(), new ArrayList<>()));
                }
            }

            //1.2 查询工单的设备列表
            List<OrderEquipPo> orderEquipPos = orderEquipService.queryByOrderIds(factory, orderIds);
            //设置进列表
            if (CollectionUtils.isNotEmpty(orderEquipPos)) {
                Map<String, List<OrderEquipPo>> cond = orderEquipPos.stream().collect(Collectors.groupingBy(OrderEquipPo::getOrderId));
                for (ExperimentVo experimentVo : vos) {
                    experimentVo.setOrderEquipPoList(cond.getOrDefault(experimentVo.getId(), new ArrayList<>()));
                }
            }

            //1.3 附件列表 由于试验报告是针对设备级别,所以同一工单下可能会有多个报告
            String[] id_arr = orderIds.toArray(new String[orderIds.size()]);

            List<Attachment> reportList = attachmentService.getAttachmentListByBusinessIdsWithType(id_arr, "report");
            //列表分组
            if (CollectionUtils.isNotEmpty(reportList)) {
                Map<String, List<Attachment>> map = reportList.stream().collect(Collectors.groupingBy(Attachment::getBusinessId));
                //设置进列表
                for (ExperimentVo experimentVo : vos) {
                    experimentVo.setAttachmentList(map.getOrDefault(experimentVo.getId(), new ArrayList<>()));
                }
            }

            // 如果选了需要作业表单则查询出作业表单中的设备信息
            vos.stream().filter(experimentVo -> Objects.equals(experimentVo.getNeedStatus(),1)).forEach(
                    experimentVo -> {
                        ExperimentEquipmentPo experimentEquipmentPo = new ExperimentEquipmentPo();
                        experimentEquipmentPo.setExperimentId(experimentVo.getId());
                        List<ExperimentEquipmentPo> experimentEquipmentPos = experimentEquipmentService.queryList(factory, experimentEquipmentPo);
                        experimentVo.setExperimentEquipmentPos(experimentEquipmentPos);
                    }
            );
        }

        return vos;
    }

    @Override
    @TaskAnnotation("submitExperiment")
    public int submitExperiment(SessionFactory factory, String orderId, String status, OrderDispatch orderDispatch) {
        ExperimentMapper mapper = factory.getMapper(ExperimentMapper.class);
        //对于工单id 及状态做处理
        ExperimentPo experimentPo = queryExperimentBeanById(factory, orderId);
        Assert.notNull(experimentPo, "工单id无效");
        String preStatus = experimentPo.getStatus();

        //将要改变的状态
        ExperimentStatus experimentStatus = ExperimentStatus.getExperimentStatus(status);
        Assert.notNull(experimentStatus, "状态修改,枚举不能为空");

        if (StringUtils.isNotEmpty(preStatus)) {
            ExperimentStatus preExperimentStatus = ExperimentStatus.getExperimentStatus(preStatus);
            Assert.notNull(preExperimentStatus, "数据有误");
            Assert.isTrue(experimentStatus.getSort() - preExperimentStatus.getSort() == 1, "不可重复提交");
        } else {
            Assert.isTrue(StringUtils.equalsAny(experimentStatus.getStatus(),
                    ExperimentStatus.DRAFT.getStatus(), ExperimentStatus.WAIT_DISPATCH.getStatus()), "修改工单状态失败");
        }

        //计划产生的则需要通知计划那边
        if (StringUtils.equalsAnyIgnoreCase("plan_order",experimentPo.getSourceId())) {
            switch (status) {
                case "IN_EXECUTE":
                    planIssueFeignService.issueExecute(experimentPo.getCode());
                    break;
                case "ARCHIVED":
                    planIssueFeignService.issueFinish(experimentPo.getCode());
                    break;
                default:
                    break;
            }
        }


        /*if (StringUtils.equals(status, ExperimentStatus.WAIT_REVIEW.getStatus())) {

            //启动工作流
            WorkflowProperties wfp = WorkflowProperties.init("experiment_wait_review");
            HashMap<String, String> map = new HashMap<>();
            //找到 工作负责人
            List<String> leaders = orderExecuteInfoService.querySubmitUserListByOrderId(factory, orderId, true);
            Assert.notEmpty(leaders, "没有找到工作负责人");
            String user = StringUtils.join(leaders.toArray(), ",");
            map.put("user", user);
            workFlowHandler.start(wfp, orderId, experimentPo.getCode(), map);
        }*/

        ExperimentPo po = new ExperimentPo();
        po.setId(orderId);
        po.setStatus(status);

        //状态变为待审核时-->设置为实际完成时间
        if (StringUtils.equals(status, ExperimentStatus.WAIT_REVIEW.getStatus())) {
            Date factEndTime = orderExecuteService.queryMaxFactEndTimeByOrderId(factory, orderId);
            po.setFactEndTime(factEndTime);
        }

        int result = mapper.update(po);
        // 试验工单发送派工通知
        if (StringUtils.equals(status, MaintainStatus.WAIT_EXECUTE.getStatus())) {
            if (com.koron.common.core.util.StringUtils.isNotEmpty(orderDispatch.getTopicWorker())) {
                String topicWorkerJson = orderDispatch.getTopicWorker();
                List<AppUserPo> userPoList = JSONUtil.parseArray(topicWorkerJson).toList(AppUserPo.class);
                String handler = userPoList.stream().map(AppUserPo::getAccount).collect(Collectors.joining(","));
                sendMessage(handler, orderDispatch, experimentPo);
            }
        }
        return result;
    }

    private void sendMessage(String handler, OrderDispatch orderDispatch, ExperimentPo experimentPo) {
        String qywxHref = messageUtil.generateQywxHref("transmit_" + orderDispatch.getOrderType() + "_order", orderDispatch.getOrderId(),"","");
        messageUtil.sendMessageWithTemplate("dispatch_notice", qywxHref, handler, experimentPo.getContext());
    }

    @Override
    @TaskAnnotation("queryPageList")
    public PageInfo<ExperimentVo> queryPageList(SessionFactory factory, ExperimentQuery query, PageQuery pageQuery) {

        PageHelper.startPage(pageQuery.getPage(), pageQuery.getPageSize(), pageQuery.getOrderBy());
        ExperimentMapper mapper = factory.getMapper(ExperimentMapper.class);

        // 向上归集查询
        if (query.isCollectionSearch()){
            PageHelper.clearPage();
            PageInfo pageInfo = collectionSearch(factory, mapper, query, pageQuery);
            return pageInfo;
        }else {
            List<ExperimentVo> list = queryList(factory, query);
            return new PageInfo<ExperimentVo>(list);
        }


    }

    private PageInfo collectionSearch(SessionFactory factory, ExperimentMapper mapper, ExperimentQuery query, PageQuery pageQuery) {
        List<ExperimentVo> result = new ArrayList<>();
        List<String> childrens = new ArrayList<>();
        getAllChildrenEquipmentIds(mapper, Arrays.asList(query.getEquipmentId()), childrens);
        childrens.add(query.getEquipmentId());
        int batch = 50;
        int totalSize = childrens.size();
        List<List<String>> collect = IntStream.iterate(0, i -> i + batch)
                .limit((totalSize + batch - 1) / batch)
                .mapToObj(start -> childrens.stream().skip(start).limit(batch).collect(Collectors.toList()))
                .collect(Collectors.toList());

        // 获取所有的设备id
        List<String> ids = collect.stream().flatMap(Collection::parallelStream).collect(Collectors.toList());
        // 获取所有的工单设备表中的设备id
        List<Map<String,Object>> orderEquipments = mapper.getAllEquipmentids();


        List<String> equipmentId = null;
        if (CollectionUtil.isNotEmpty(orderEquipments)){
            equipmentId = orderEquipments.stream().map(map -> (String) map.get("equipmentId")).collect(Collectors.toList());
        }
        // 取交集，获取到设备id
        Collection<String> intersection = CollectionUtil.intersection(ids, equipmentId);
        // 获取试验工单id
        List<String> experimentIds = new ArrayList<String>();
        orderEquipments.forEach(entry -> {
                intersection.forEach(eId -> {
                    if (eId.equals(entry.get("equipmentId"))){
                        experimentIds.add((String) entry.get("orderId"));
                    }
                });
        });
        // 按条件分页查询
        PageHelper.startPage(pageQuery.getPage(), pageQuery.getPageSize(), pageQuery.getOrderBy());
        query.setExperimentIds(experimentIds);

        List<ExperimentVo> list = mapper.queryList(query);
        if (CollectionUtils.isNotEmpty(list)){
                List<String> orderIds = list.stream().map(ExperimentVo::getId).collect(Collectors.toList());
                list.stream().forEach(p-> {
                    p.setExperimentEquipmentPos(new ArrayList<>());

                    String trafficLight = CommonCodeTools.calcTrafficLight(p.getFactEndTime(), p.getStartTime(), p.getEndTime(), p.getStatus());
                    p.setTrafficLight(trafficLight);
                });

                //1.1 查询工单的作业单位
                List<OrderWorkDeptPo> orderWorkDeptPos = workDeptService.queryByOrderIds(factory, orderIds);
                //设置进列表
                if (CollectionUtils.isNotEmpty(orderWorkDeptPos)) {
                    Map<String, List<OrderWorkDeptPo>> cond = orderWorkDeptPos.stream().collect(Collectors.groupingBy(OrderWorkDeptPo::getOrderId));
                    for (ExperimentVo experimentVo : list) {
                        experimentVo.setWorkDeptPoList(cond.getOrDefault(experimentVo.getId(), new ArrayList<>()));
                    }
                }
            result.addAll(list);
        }
        return new PageInfo<>(result);
    }

    private List<String> getAllChildrenEquipmentIds(ExperimentMapper mapper, List<String> equipmentIds, List<String> result) {
        if (CollectionUtils.isEmpty(equipmentIds)){
            return new ArrayList<>();
        }
        List<String> children = mapper.getAllChildrenEquipmentIds(equipmentIds);

        result.addAll(children);
        return getAllChildrenEquipmentIds(mapper, children, result);
    }

    @Override
    @TaskAnnotation("insertOrUpdateDetail")
    public String insertOrUpdateDetail(SessionFactory factory, ExperimentDetailDto dto) {

        //1.1 基础信息
        ExperimentPo experimentPo = experimentConvertor.dtoToBean(dto);

        experimentPo.setStatus(dto.getIsCommit() ? ExperimentStatus.WAIT_DISPATCH.getStatus() : ExperimentStatus.DRAFT.getStatus());

        String experimentId;

        if (StringUtils.isEmpty(experimentPo.getId())) {
            experimentId = CodeTools.getCode32();
            experimentPo.setId(experimentId);
            experimentPo.setCode(CommonCodeTools.getNormalOrderCode(OrderCodeEnum.SY));
            insert(factory, experimentPo);
        } else {
            experimentId = experimentPo.getId();
            update(factory, experimentPo);
        }

        //1.2 作业准备
        List<ExperimentReadyPo> experimentReadyPoList = dto.getExperimentReadyPoList();
        if (!CollectionUtils.isEmpty(experimentReadyPoList)) {
            experimentReadyPoList.forEach(p -> {
                p.setExperimentId(experimentId);
            });
            experimentReadyService.insertBatch(factory, experimentReadyPoList);
        }else {
            experimentReadyService.deleteByOrderId(factory,experimentId);
        }
        //1.3 作业过程
        List<ExperimentEquipmentPo> experimentEquipmentPoList = dto.getExperimentEquipmentPoList();
        if (!CollectionUtils.isEmpty(experimentEquipmentPoList)) {
            experimentEquipmentPoList.forEach(p -> {
                p.setExperimentId(experimentId);
            });
            experimentEquipmentService.insertBatch(factory, experimentEquipmentPoList);
        }else {
            experimentEquipmentService.deleteByOrderId(factory,experimentId);
        }
        //1.4 作业指导书
        List<OrderInstructionBean> orderInstructionBeanList = dto.getOrderInstructionBeanList();
        if (!CollectionUtils.isEmpty(orderInstructionBeanList)) {
            orderInstructionBeanList.forEach(p -> {
                p.setBussinessId(experimentId);
                p.setOrderType(OrderType.EXPERIMENT);
            });
            orderInstructionService.insertBatch(factory, orderInstructionBeanList);
        }else {
            orderInstructionService.deleteByBizId(factory, experimentId, OrderType.EXPERIMENT);
        }
        //1.5 设备列表
        List<OrderEquipPo> orderEquipPoList = dto.getOrderEquipPoList();
        if (!CollectionUtils.isEmpty(orderEquipPoList)) {
            orderEquipPoList.forEach(p -> {
                p.setOrderId(experimentId);
                p.setOrderType(OrderType.EXPERIMENT);
            });
            //设备
            orderEquipService.insertBatch(factory, orderEquipPoList);
        }else {
            orderEquipService.deleteByOrderId(factory,experimentId);
        }
        //1.6 工作地点
        List<OrderLocationPo> orderLocationList = dto.getOrderLocationList();
        String location = experimentPo.getLocation();
        String locationName = experimentPo.getLocationName();

        if (!StringUtils.isAllEmpty(location,locationName)) {
            OrderLocationPo orderLocationPo = new OrderLocationPo();
            orderLocationPo.setLocation(location);
            orderLocationPo.setLocationName(locationName);
            orderLocationList.add(orderLocationPo);
        }
        if (CollectionUtils.isNotEmpty(orderLocationList)) {
            orderLocationList.forEach(p -> {
                p.setOrderId(experimentId);
                p.setOrderType(OrderType.EXPERIMENT);
            });
            orderLocationService.insertBatch(factory, orderLocationList);
        } else {
            orderLocationService.deleteByOrderId(factory, experimentId);
        }

        //1.7 生成项目及记录项记录
        executeService.generateStand(factory, experimentId);

        return experimentId;
    }

    @Override
    @TaskAnnotation("queryExperimentBeanById")
    public ExperimentPo queryExperimentBeanById(SessionFactory factory, String orderId) {

        ExperimentMapper mapper = factory.getMapper(ExperimentMapper.class);
        ExperimentPo experimentPo = mapper.queryById(orderId);
        return experimentPo;
    }

    @Override
    @TaskAnnotation("queryReviewInfoByOrderId")
    public ReviewVo queryReviewInfoByOrderId(SessionFactory factory, String orderId) {

        ReviewVo reviewVo = new ReviewVo();
        //1.主表信息
        ExperimentPo experimentPo = queryExperimentBeanById(factory, orderId);
        Assert.isTrue(experimentPo != null, "没有找到该工单信息");
        BeanUtils.copyProperties(experimentPo, reviewVo);

        //2.设备及结论信息
        ExperimentEquipmentPo equipmentPo = new ExperimentEquipmentPo();
        equipmentPo.setExperimentId(orderId);
        List<ExperimentEquipmentPo> experimentEquipmentPos = experimentEquipmentService.queryList(factory, equipmentPo);
        List<EquipmentConclusionVo> equipmentConclusionVos = experimentConvertor.EquipmentPoListToVoList(experimentEquipmentPos);
        reviewVo.setEquipmentConclusionVoList(equipmentConclusionVos);

        return reviewVo;
    }

}
